package wechat

import (
	"anban/utils"
	"crypto/tls"
	"crypto/x509"
	"encoding/xml"
	"fmt"
	"github.com/astaxie/beego"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
)

type EnterprisePayment struct {
	XMLName        xml.Name `xml:"xml"`              // XML根节点名字
	MchAppid       string   `xml:"mch_appid"`        // 申请商户号的appid或商户号绑定的appid
	Mchid          string   `xml:"mchid"`            // 微信支付分配的商户号
	NonceStr       string   `xml:"nonce_str"`        // 随机字符串，不长于32位
	Sign           string   `xml:"sign"`             // 签名
	PartnerTradeNo string   `xml:"partner_trade_no"` // 商户订单号，需保持唯一性 (只能是字母或者数字，不能包含有其它字符)
	Openid         string   `xml:"openid"`           // 商户appid下，某用户的openid
	CheckName      string   `xml:"check_name"`       // NO_CHECK：不校验真实姓名 FORCE_CHECK：强校验真实姓名
	Amount         int      `xml:"amount"`           // 企业付款金额，单位为分
	Desc           string   `xml:"desc"`             // 企业付款备注，必填。注意：备注中的敏感词会被转成字符*
	SpbillCreateIp string   `xml:"spbill_create_ip"` // 该IP同在商户平台设置的IP白名单中的IP没有关联，该IP可传用户端或者服务端的IP。
}

type EnterprisePaymentRes struct {
	XMLName        xml.Name `xml:"xml"`
	ReturnCode     string   `xml:"return_code"`      // SUCCESS/FAIL 此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
	ReturnMsg      string   `xml:"return_msg"`       // 返回信息，如非空，为错误原因
	MchAppid       string   `xml:"mch_appid"`        // 申请商户号的appid或商户号绑定的appid（企业号corpid即为此appId）
	Mchid          string   `xml:"mchid"`            // 微信支付分配的商户号
	DeviceInfo     string   `xml:"device_info"`      // 微信支付分配的终端设备号
	NonceStr       string   `xml:"nonce_str"`        // 随机字符串，不长于32位
	ResultCode     string   `xml:"result_code"`      // SUCCESS/FAIL，注意：当状态为FAIL时，存在业务结果未明确的情况。如果状态为FAIL，请务必关注错误代码（err_code字段），通过查询查询接口确认此次付款的结果。
	ErrCode        string   `xml:"err_code"`         // 错误码信息，注意：出现未明确的错误码时（SYSTEMERROR等），请务必用原商户订单号重试，或通过查询接口确认此次付款的结果。
	ErrCodeDes     string   `xml:"err_code_des"`     // 结果信息描述
	PartnerTradeNo string   `xml:"partner_trade_no"` // 商户订单号，需保持历史全局唯一性(只能是字母或者数字，不能包含有其它字符)
	PaymentNo      string   `xml:"payment_no"`       // 企业付款成功，返回的微信付款单号
	PaymentTime    string   `xml:"payment_time"`     // 企业付款成功时间
}

func NewEnterprisePayment(amount int, openid, tradeNo, desc string) *EnterprisePayment {
	mchAppid := beego.AppConfig.String("wechatappid")
	mchid := beego.AppConfig.String("mchid")
	nonceStr := utils.GetRandomString(32)
	apiKey := beego.AppConfig.String("apikey")
	enterprisePayment := &EnterprisePayment{
		MchAppid:       mchAppid,
		Mchid:          mchid,
		NonceStr:       nonceStr,
		Sign:           "",
		PartnerTradeNo: tradeNo,
		Openid:         openid,
		CheckName:      "NO_CHECK",
		Amount:         amount,
		Desc:           desc,
		SpbillCreateIp: "127.0.0.1",
	}
	enterprisePaymentMap := map[string]interface{}{
		"mch_appid":        enterprisePayment.MchAppid,
		"mchid":            enterprisePayment.Mchid,
		"nonce_str":        enterprisePayment.NonceStr,
		"partner_trade_no": enterprisePayment.PartnerTradeNo,
		"openid":           enterprisePayment.Openid,
		"check_name":       enterprisePayment.CheckName,
		"amount":           enterprisePayment.Amount,
		"desc":             enterprisePayment.Desc,
		"spbill_create_ip": enterprisePayment.SpbillCreateIp,
	}
	keySlice := []string{}
	for k, _ := range enterprisePaymentMap {
		keySlice = append(keySlice, k)
	}
	sort.Strings(keySlice)
	paramStr := ""
	for _, v := range keySlice {
		if v == "amount" {
			paramStr += fmt.Sprintf("%s=%d&", v, enterprisePaymentMap[v])
			continue
		}
		paramStr += fmt.Sprintf("%s=%s&", v, enterprisePaymentMap[v])
	}
	paramStr += fmt.Sprintf("key=%s", apiKey)
	sign := strings.ToUpper(utils.Encode(paramStr))
	enterprisePayment.Sign = sign
	return enterprisePayment
}

//企业付款到零钱
func (e *EnterprisePayment) Pay() (*EnterprisePaymentRes, error) {
	contentType := "application/xml;charset=utf-8"
	url := "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"
	wechatPayCert := "./cert/apiclient_cert.pem"
	wechatPayKey := "./cert/apiclient_key.pem"
	var tr *http.Transport
	certs, err := tls.LoadX509KeyPair(wechatPayCert, wechatPayKey)
	if err != nil {
		return nil, utils.NewError(0, "无法加载微信支付证书")
	}
	rootCa, err := ioutil.ReadFile("./cert/rootca.pem")
	if err != nil {
		return nil, utils.NewError(0, "无法加载微信支付ROOT CA证书")
	}
	pool := x509.NewCertPool()
	pool.AppendCertsFromPEM(rootCa)
	tr = &http.Transport{
		TLSClientConfig: &tls.Config{
			RootCAs:      pool,
			Certificates: []tls.Certificate{certs},
		},
	}
	client := &http.Client{Transport: tr}
	content, err := xml.Marshal(e)
	if err != nil {
		return nil, utils.NewError(0, "企业付款到零钱结构体无法转为XML")
	}
	resp, err := client.Post(url, contentType, strings.NewReader(string(content)))
	if err != nil {
		return nil, utils.NewError(0, "企业付款到零钱请求失败")
	}
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	enterprisePaymentRes := EnterprisePaymentRes{}
	err = xml.Unmarshal(body, &enterprisePaymentRes)
	if err != nil {
		return nil, utils.NewError(0, "企业付款到零钱返回内容无法解析为结构体")
	}
	return &enterprisePaymentRes, nil
}
